// 4. 常⽤类型

// 4.1. 字⾯量
let a: '你好' //a的值只能为字符串“你好”
let b: 100 //b的值只能为数字100
// a = '欢迎'//警告：不能将类型“"欢迎"”分配给类型“"你好"”
// b = 200 //警告：不能将类型“200”分配给类型“100”

let gender: '男'|'⼥' //定义⼀个gender变量，值只能为字符串“男”或“⼥”
gender = '男'
// gender = '未知' //不能将类型“"未知"”分配给类型“"男" | "⼥"”

// 4.2. any
// any 的含义是：任意类型，⼀旦将变量类型限制为 any ，那就意味着放弃了对该变量的类型检查。
// 明确的表示aa的类型是any —— 显式的any
let aa: any
//以下对aa的赋值，均⽆警告
aa = 100
aa = '你好'
aa = false
//没有明确的表示b的类型是any，但TS主动推断了出来 —— 隐式的any
let bb
//以下对b的赋值，均⽆警告
bb = 100
bb = '你好'
bb = false

// 注意点： any 类型的变量，可以赋值给任意类型的变量
/* 注意点：any类型的变量，可以赋值给任意类型的变量 */
let aaa
let x: string
x = aaa // ⽆警告

// 4.3. unknown
// unknown 的含义是：未知类型。
// 备注1： unknown 可以理解为⼀个类型安全的 any
// 备注2： unknown 适⽤于：开始不知道数据的具体类型，后期才能确定数据的类型

// 设置a的类型为unknown
let a2: unknown
//以下对a的赋值，均正常 a = 100
a2 = false
a2 = '你好'
// 设置x的数据类型为string
let x2: string
// x2 = a2 //警告：不能将类型“unknown”分配给类型“string”

// 若就是想把 a 赋值给 x ，可以⽤以下三种写法：
/*
    // 设置a的类型为unknown
    let a: unknown
    a = 'hello'
    //第⼀种⽅式：加类型判断
    if(typeof a === 'string'){
        x = a }
    //第⼆种⽅式：加断⾔ x = a as string
    //第三种⽅式：加断⾔ x = <string>a
* */
let a3: unknown
a3 = 'hello'
if (typeof a3 === 'string'){
    x2 = a3
}
let x3: string
x3 = a3 as string

let x5: string
x5 = <string>a3

// 4.4. never
// never 的含义是：任何值都不是，简⾔之就是不能有值， undefined 、 null 、 '' 、 0 都不 ⾏！

// 1. ⼏乎不⽤ never 去直接限制变量，因为没有意义，例如：
/* 指定a的类型为never，那就意味着a以后不能存任何的数据了 */
let a11: never
// 以下对a11的所有赋值都会有警告
// a11 = 1
// a11 = true
// a11 = undefined
// a11 = null

// 2. never ⼀般是 TypeScript 主动推断出来的，例如：
// 指定a22的类型为string
let a22: string
// 给a22设置⼀个值 a22 = 'hello'
if(typeof a22 === 'string'){
    a22.toUpperCase()
}else{
    console.log(a22) // TypeScript会推断出此处的a22是never，因为没有任何⼀个值符合此处的逻辑
}

// 3. never 也可⽤于限制函数的返回值
// 限制demo函数不需要有任何返回值，任何值都不⾏，像undeifned、null都不⾏
function demo():never{
    throw new Error('程序异常退出')
}

// 4.5. void
// void 的含义是： 空 或 undefined ，严格模式下不能将 null 赋值给 void 类型。
let a33:void = undefined
//严格模式下，该⾏会有警告：不能将类型“null”分配给类型“void”
let b33:void = null

// void 常⽤于限制函数返回值
// ⽆警告
function demo1():void{ }
// ⽆警告
function demo2():void{
    return
}
// ⽆警告
function demo3():void{
    return undefined
}
// 有警告：不能将类型“number”分配给类型“void”
function demo4():void{
    // return 666
}

// 4.6. object 关于 Object 与 object ，直接说结论：在类型限制时， Object ⼏乎不⽤，因为范围太⼤了，⽆ 意义
// 1. object 的含义：任何【⾮原始值类型】，包括：对象、函数、数组等，限制的范围⽐较宽泛，⽤的少。

let a222:object //a222的值可以是任何【⾮原始值类型】，包括：对象、函数、数组等
// 以下代码，是将【⾮原始类型】赋给a222，所以均⽆警告
a222 = {}
a222 = {name:'张三'}
a222 = [1,3,5,7,9]
a222 = function(){}
// 以下代码，是将【原始类型】赋给a，有警告
a222 = null // 警告：不能将类型“null”分配给类型“object”  ???
a222 = undefined // 警告：不能将类型“undefined”分配给类型“object” ???
// a222 = 1 // 警告：不能将类型“number”分配给类型“object”
// a222 = true // 警告：不能将类型“boolean”分配给类型“object”
// a222 = '你好' // 警告：不能将类型“string”分配给类型“object”

// 2. Object 的含义： Object 的实例对象，限制的范围太⼤了，⼏乎不⽤。
let a333:Object //a的值必须是Object的实例对象，
// 以下代码，均⽆警告，因为给a赋的值，都是Object的实例对象 s
a333 = {}
a333 = {name:'张三'}
a333 = [1,3,5,7,9]
a333 = function(){}
a333 = 1 // 1不是Object的实例对象，但其包装对象是Object的实例
a333 = true // truue不是Object的实例对象，但其包装对象是Object的实例
a333 = '你好' // “你好”不是Object的实例对象，但其包装对象是Object的实例
// 以下代码均有警告
a333 = null // 警告：不能将类型“null”分配给类型“Object” ???
a333 = undefined // 警告：不能将类型“undefined”分配给类型“Object” ???

// 3. 实际开发中，限制⼀般对象，通常使⽤以下形式
// 限制person对象的具体内容，使⽤【,】分隔，问号代表可选属性
let person: { name: string, age?: number}
// 限制car对象的具体内容，使⽤【;】分隔，必须有price和color属性，其他属性不去限制，有 没有都⾏
let car: {price: number; color: string; [k:string]:any}
// 限制student对象的具体内容，使⽤【回⻋】分隔
let student: {
    id: string
    grade: number
}
// 以下代码均⽆警告
person = {name:'张三',age:99}
console.log(person)
person = {name:'李四'}
console.log(person)
car = {price:100,color:'red'}
console.log(car)
car = {price:100,color:'red',x:12,y:true,z:'你好'}
console.log(car)

student = {id:'xxx001',grade:333}
console.log(student)
// student = {id:'xxx001',grade:333,name:'x'}


// 4. 限制函数的参数、返回值，使⽤以下形式
let demoxx:(a:number,b:number)=>number

demoxx = (x,y)=>{
    return x+y
}
console.log(demoxx(1,3))

// 5. 限制数组，使⽤以下形式
let arr1: string[] // 该⾏代码等价于： let arr1: Array<string>
let arr2: number[] // 该⾏代码等价于： let arr2: Array<number>
arr1 = ['a','b','c']
arr2 = [1,3,5,7,9]
console.log(arr1,arr2)

// 4.7. tuple
// tuple 就是⼀个⻓度固定的数组。
let t: [string,number]
t = ['hello',123]
// 警告，不能将类型“[string, number, boolean]”分配给类型“[string, number]”
// t = ['hello',123,false]
console.log(t,typeof(t))

// 4.8. enum
// enum 是枚举
// 定义⼀个枚举
enum Color {
    Red,
    Blue,
    Black,
    Gold
}
console.log(Color)
// 定义⼀个枚举，并指定其初识数值
enum Color2 {
    Red = 6,
    Blue,
    Black,
    Gold
}
console.log(Color2)

// 定义⼀个phone变量，并设置对⻬进⾏限制
let phone: {name:string,price:number,color:Color}
phone = {name:'华为Mate60',price:6500,color:Color.Red}
console.log(phone)
phone = {name:'iphone',price:1000,color:Color.Black}
console.log(phone)
if (phone.color === Color.Black){
    console.log('手机是黑色的')
}
